////////////////////////////////////////////////////////////////////////////////
//	Copyright (c) 2004 by Michael 'Astar' Sotnikov
//	astar@tut.by
////////////////////////////////////////////////////////////////////////////////
#include "cSmartAssert.hpp"

#include <iostream>
#include <sstream>

////////////////////////////////////////////////////////////////////////////////
AssertContext_c::AssertContext_c()
:	m_level	( LVL_ERROR )
,	m_line	( 0 )
{
}

void AssertContext_c::setLevel( const AssertLevel_e lvl )
{
	m_level = lvl;
}

AssertLevel_e AssertContext_c::getLevel() const
{
	return m_level;
}

void AssertContext_c::setFile( const char* file )
{
	m_file = file;
}

void AssertContext_c::setFile( const std::string& file )
{
	m_file = file;
}

const std::string& AssertContext_c::getFile() const
{
	return m_file;
}

void AssertContext_c::setFunc( const char* func )
{
	m_func = func;
}

void AssertContext_c::setFunc( const std::string& func )
{
	m_func = func;
}

const std::string& AssertContext_c::getFunc() const
{
	return m_func;
}

void AssertContext_c::setLine( unsigned int line )
{
	m_line = line;
}

unsigned int AssertContext_c::getLine() const
{
	return m_line;
}

void AssertContext_c::setDesc( const char* desc )
{
	m_desc = desc;
}

void AssertContext_c::setDesc( const std::string& desc )
{
	m_desc = desc;
}

const std::string& AssertContext_c::getDesc() const
{
	return m_desc;
}

void AssertContext_c::setExpr( const char* expr )
{
	m_expr = expr;
}

void AssertContext_c::setExpr( const std::string& expr )
{
	m_expr = expr;
}

const std::string& AssertContext_c::getExpr() const
{
	return m_expr;
}

void AssertContext_c::addValue( const char* name, const std::string& value )
{
	m_values.push_back( std::make_pair( name, value ) );
}

void AssertContext_c::addValue( const std::string& name, const std::string& value )
{
	m_values.push_back( std::make_pair( name, value ) );
}
	
const AssertContext_c::ValsArray_t& AssertContext_c::getValues() const
{
	return m_values;
}

const std::string AssertContext_c::getMsg() const
{
	std::ostringstream out;
	try{
		out << "[" << m_file << ":" << m_line << " (" << m_func << ") ] ";
		
		switch ( m_level ){
			case LVL_WARN:		out << "Warn: ";		break;
			case LVL_ERROR:		out << "Error: ";		break;
			case LVL_FATAL:		out << "Fatal: ";		break;
			default:			out << "Unknown: ";		break;
		}
		out << m_desc << std::endl;
		if( !m_expr.empty() )
			out << m_expr << " failed" << std::endl;
		for( ValsArray_t::const_iterator it = m_values.begin(); it != m_values.end(); ++it )
			out << (*it).first << " = " << (*it).second << std::endl;
		out << std::flush;
	}
	catch( ... )
	{
		// guarantee stability
	}
	
	return out.str();
}

////////////////////////////////////////////////////////////////////////////////
AssertException_s::AssertException_s( const AssertContext_c& context )
:	m_context( context )
{
}

AssertException_s::~AssertException_s() throw()
{}

const char* AssertException_s::what() const throw()
{
	return m_context.getMsg().c_str();
}

////////////////////////////////////////////////////////////////////////////////
SmartAssert_c::HandlerCallback_t SmartAssert_c::m_callbacks[LVL_TOTAL] =
	{	defaultWarnHandler, defaultErrorHandler, defaultFatalHandler	};

SmartAssert_c::~SmartAssert_c()
{
	const AssertLevel_e level = m_context.getLevel();
	if ( ( level >= LVL_WARN ) && ( level < LVL_TOTAL ) )
	{
		if ( m_callbacks[level] ) 
			m_callbacks[level]( m_context );
	}
	else
		abort();	// something gonna very bad
}

SmartAssert_c& SmartAssert_c::setContext( const char* file, const char* func, const unsigned int line )
{
#if defined(WIN32)
	static const char* dir_separator = "\\";
#else
	static const char* dir_separator = "/";
#endif // defined(WIN32)

	std::string sfile( file );
	const std::string::size_type nPos = sfile.find_last_of( dir_separator );
	m_context.setFile( ( nPos == std::string::npos ) ? file : sfile.substr( nPos + 1 ) );
	
	m_context.setFunc( func );
	m_context.setLine( line );
	return( *this );
}

SmartAssert_c& SmartAssert_c::setExpr( const char* expr )
{
	m_context.setExpr( expr );	
	return( *this );
}

SmartAssert_c& SmartAssert_c::warn( const char* desc )
{
	m_context.setLevel( LVL_WARN );
	m_context.setDesc( desc );
	return( *this );
}

SmartAssert_c& SmartAssert_c::warn( const std::string& desc )
{
	m_context.setLevel( LVL_WARN );
	m_context.setDesc( desc );
	return( *this );
}

SmartAssert_c& SmartAssert_c::error( const char* desc )
{
	m_context.setLevel( LVL_ERROR );
	m_context.setDesc( desc );
	return( *this );
}

SmartAssert_c& SmartAssert_c::error( const std::string& desc )
{
	m_context.setLevel( LVL_ERROR );
	m_context.setDesc( desc );
	return( *this );
}

SmartAssert_c& SmartAssert_c::fatal( const char* desc )
{
	m_context.setLevel( LVL_FATAL );
	m_context.setDesc( desc );
	return( *this );
}

SmartAssert_c& SmartAssert_c::fatal( const std::string& desc )
{
	m_context.setLevel( LVL_FATAL );
	m_context.setDesc( desc );
	return( *this );
}

void SmartAssert_c::defaultWarnHandler( const AssertContext_c& context )
{
	std::cerr << context.getMsg();
}

void SmartAssert_c::defaultErrorHandler( const AssertContext_c& context )
{
	std::cerr << context.getMsg();
	throw AssertException_s( context );
}

void SmartAssert_c::defaultFatalHandler( const AssertContext_c& context )
{
	std::cerr << context.getMsg();
	abort();
}

void SmartAssert_c::setHandler(	AssertLevel_e level, HandlerCallback_t callback )
{
	SMART_ASSERT( ( level >= LVL_WARN ) && ( level < LVL_TOTAL ) )
		.error( "Bad assertion level." ).addValue( "level", level );
		
	SMART_ASSERT( callback != NULL )
		.warn( "Assertion handling for specified level will be disabled" )
		.addValue( "level", level );
	
	m_callbacks[level] = callback;
}

